// ---
//
// $Id: mytest.cpp,v 1.5 2008/07/11 16:49:43 hartwork Exp $
//
// CppTest - A C++ Unit Testing Framework
// Copyright (c) 2003 Niklas Lundell
//
// ---
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// ---
//
// Test program demonstrating all assert types and output handlers.
//
// ---

#include <cstdlib>
#include <cstring>
#include <iostream>

#ifdef _MSC_VER
	#pragma warning (disable: 4290)
#endif

#include "cpptest.h"
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <objbase.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <string>
#include <wininet.h>
#include <initguid.h>
#include "../WebAggregator/WebKit.tlh"
#include "../WebAggregator/ILayoutAwareObject.h"
#include "../WebAggregator/IDocument.h"
#include "../WebAggregator/IDocumentProvider.h"

DEFINE_GUID(CLSID_WebView,0xd6bca079,0xf61c,0x4e1e,0xb4,0x53,0x32,0xa0,0x47,0x7d,0x02,0xe3);
DEFINE_GUID(IID_IWebView,0x174BBEFD,0x058E,0x49c7,0x91,0xDF,0x6F,0x11,0x0A,0xA4,0xAC,0x28);
DEFINE_GUID(IID_IDOMElementPrivate,0x7FF28EC8,0x6DA1,0x498d,0x9A,0x43,0x7E,0x67,0xC2,0xD3,0x12,0x8F);
DEFINE_GUID(IID_IWebViewPrivate,0x44914369,0xDEB5,0x4fcf,0xA6,0xA3,0x30,0xC0,0x2E,0x73,0x15,0x4F);
DEFINE_GUID(IID_IWebFrameLoadDelegate,0x3354665B,0x84BA,0x4fdf,0xB3,0x5E,0xBF,0x5C,0xF9,0xD9,0x60,0x26);
DEFINE_GUID(IID_IWebMutableURLRequest,0xC4042773,0x371F,0x427e,0xAF,0xA9,0x9D,0x4B,0x35,0x8A,0x0D,0x93);
DEFINE_GUID(CLSID_WebMutableURLRequest,0xa062ecc3,0xbb1b,0x4694,0xa5,0x69,0xf5,0x9e,0x0a,0xd6,0xbe,0x0c);

using namespace std;
using namespace WebKit;
using namespace WebAggregator::dataGrabber;

#define MAX_LOADSTRING 100
#define URLBAR_HEIGHT  24

// Global Variables:
HINSTANCE hInst;                                // current instance
HWND gViewWindow;
HWND hwndMain;
WNDPROC DefEditProc = 0;
WNDPROC DefWebKitProc = 0;
IWebView* gWebView = 0;
IWebViewPrivate* gWebViewPrivate = 0;

// Support moving the transparent window
POINT s_windowPosition = { 100, 100 };
SIZE s_windowSize = { 800, 400 };
bool s_usesLayeredWebView = false;
bool s_fullDesktop = false;
TCHAR szTitle[MAX_LOADSTRING];                    // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

typedef HRESULT (STDAPICALLTYPE *MyDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);

class WebKitNativeController : public IWebFrameLoadDelegate
	{

	protected:
		ULONG m_refCount;

	public:
		WebKitNativeController() : m_refCount(1) 
		{

		}

		~WebKitNativeController()
		{

		}
		/*
		// IUnknown
		virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject){ return S_OK; }
		virtual ULONG STDMETHODCALLTYPE AddRef(void){ return S_OK; }
		virtual ULONG STDMETHODCALLTYPE Release(void){ return S_OK; }
		*/

		virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
		{
			*ppvObject = 0;
			if (IsEqualGUID(riid, IID_IUnknown))
				*ppvObject = static_cast<IWebFrameLoadDelegate*>(this);
			else if (IsEqualGUID(riid, IID_IWebFrameLoadDelegate))
				*ppvObject = static_cast<IWebFrameLoadDelegate*>(this);
			else
				return E_NOINTERFACE;

			AddRef();
			return S_OK;
		}

		virtual ULONG STDMETHODCALLTYPE AddRef(void)
		{
			return ++m_refCount;
		}

		virtual ULONG STDMETHODCALLTYPE Release(void)
		{
			ULONG newRef = --m_refCount;
			if (!newRef)
				delete(this);

			return newRef;
		}

		// IWebFrameLoadDelegate
		virtual HRESULT STDMETHODCALLTYPE didStartProvisionalLoadForFrame( 
			/* [in] */ IWebView* webView,
			/* [in] */ IWebFrame* /*frame*/) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didReceiveServerRedirectForProvisionalLoadForFrame( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didFailProvisionalLoadWithError( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebError *error,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didCommitLoadForFrame( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didReceiveTitle( 
			/* [in] */ IWebView *webView,
			/* [in] */ _bstr_t title,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didReceiveIcon( 
			/* [in] */ IWebView *webView,
			/* [in] */ OLE_HANDLE hBitmap,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didFinishLoadForFrame( 
			/* [in] */ IWebView* webView,
			/* [in] */ IWebFrame* /*frame*/) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didFailLoadWithError( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebError *error,
			/* [in] */ IWebFrame *forFrame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didChangeLocationWithinPageForFrame( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebFrame *frame) { return S_OK; }

		virtual HRESULT STDMETHODCALLTYPE willPerformClientRedirectToURL( 
			/* [in] */ IWebView *webView,
			/* [in] */ _bstr_t url,
			/* [in] */ double delaySeconds,
			/* [in] */ DATE fireDate,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE didCancelClientRedirectForFrame( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual HRESULT STDMETHODCALLTYPE willCloseFrame( 
			/* [in] */ IWebView *webView,
			/* [in] */ IWebFrame *frame) { return S_OK; }
    
		virtual /* [local] */ HRESULT STDMETHODCALLTYPE windowScriptObjectAvailable( 
			/* [in] */ IWebView *webView,
			/* [in] */ void* context,
			/* [in] */ void* windowScriptObject)  { return S_OK; }

		virtual HRESULT STDMETHODCALLTYPE didClearWindowObject (
			/*[in]*/ struct IWebView * WebView,
			/*[in]*/ void * context,
			/*[in]*/ void * windowScriptObject,
			/*[in]*/ struct IWebFrame * frame ) { return S_OK; }

		// WinLauncherWebHost

	  virtual HRESULT STDMETHODCALLTYPE raw_didStartProvisionalLoadForFrame (
        /*[in]*/ struct IWebView * WebView,
		/*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didReceiveServerRedirectForProvisionalLoadForFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didFailProvisionalLoadWithError (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebError * error,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didCommitLoadForFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didReceiveTitle (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ BSTR title,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didReceiveIcon (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ OLE_HANDLE hBitmap,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didFinishLoadForFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didFailLoadWithError (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebError * error,
        /*[in]*/ struct IWebFrame * forFrame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didChangeLocationWithinPageForFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_willPerformClientRedirectToURL (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ BSTR url,
        /*[in]*/ double delaySeconds,
        /*[in]*/ DATE fireDate,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didCancelClientRedirectForFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_willCloseFrame (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_windowScriptObjectAvailable (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ void * context,
        /*[in]*/ void * windowScriptObject ) { return S_OK; }
      virtual HRESULT STDMETHODCALLTYPE raw_didClearWindowObject (
        /*[in]*/ struct IWebView * WebView,
        /*[in]*/ void * context,
        /*[in]*/ void * windowScriptObject,
        /*[in]*/ struct IWebFrame * frame ) { return S_OK; }

		// WinLauncherWebHost
	};

IDOMDocument* domDoc = NULL;

// Tests unconditional fail asserts
//
class FailTestSuite : public Test::Suite
{
public:
	FailTestSuite()
	{
		TEST_ADD(FailTestSuite::success)
		TEST_ADD(FailTestSuite::always_fail)
	
	}
	
private:
	void success() {}
	
	void always_fail()
	{
		// This will always fail
		//
		TEST_FAIL("unconditional fail");
	}
};

// Tests compare asserts
//
class CompareTestSuite : public Test::Suite
{
public:
	CompareTestSuite()
	{
		//TEST_ADD(CompareTestSuite::success)
		TEST_ADD(CompareTestSuite::compare)
		//TEST_ADD(CompareTestSuite::delta_compare)
	}
	
private:
	//void success() {}

	void compare()
	{
		// Will succeed since the expression evaluates to true
		//
		//TEST_ASSERT(1 + 1 == 2)
		
		IDocumentProvider documentProvider;
		documentProvider.initialize();
		IDocument* doc = documentProvider.fetch();

		if(doc != NULL)
		{
			doc->initialize();
			doc->load(domDoc);
			
			ILayoutObjectCollection* col = doc->getElements();
			if(col != NULL)
			{
				ILayoutAwareObject* obj = NULL;
				for(int i=0; i<col->layoutAwareObjects.size(); i++)
				{
					if(col->layoutAwareObjects.at(i)->value().find("News in:") != std::string::npos)
					{
						obj = col->layoutAwareObjects.at(i);
						break;
					}
				}

				if(obj != NULL)
				{
					ILayoutAwareObject* rightNeighbor = obj->neighbor_right();
					while(strcmp(rightNeighbor->value().c_str(), "") == 0)
						rightNeighbor = rightNeighbor->neighbor_right();
					TEST_ASSERT(rightNeighbor->value().find("English") != std::string::npos);
				}
			}

			col->unInitialize();
			doc->unInitialize();
			delete doc;
		}
		documentProvider.unInitialize();
		// Will fail since the expression evaluates to false
		//
		//TEST_ASSERT(0 == 1);
	}
	/*
	void delta_compare()
	{
		// Will succeed since the expression evaluates to true
		//
		TEST_ASSERT_DELTA(0.5, 0.7, 0.3);
		
		// Will fail since the expression evaluates to false
		//
		TEST_ASSERT_DELTA(0.5, 0.7, 0.1);
	}*/
};

// Tests throw asserts
//
class ThrowTestSuite : public Test::Suite
{
public:
	ThrowTestSuite()
	{
		TEST_ADD(ThrowTestSuite::success)
		TEST_ADD(ThrowTestSuite::test_throw)
	}
	
private:
	void success() {}
	
	void test_throw()
	{
		// Will fail since the none of the functions throws anything
		//
		TEST_THROWS_MSG(func(), int, "func() does not throw, expected int exception")
		TEST_THROWS_MSG(func_no_throw(), int, "func_no_throw() does not throw, expected int exception")
		TEST_THROWS_ANYTHING_MSG(func(), "func() does not throw, expected any exception")
		TEST_THROWS_ANYTHING_MSG(func_no_throw(), "func_no_throw() does not throw, expected any exception")
		
		// Will succeed since none of the functions throws anything
		//
		TEST_THROWS_NOTHING(func())
		TEST_THROWS_NOTHING(func_no_throw())
		
		// Will succeed since func_throw_int() throws an int
		//
		TEST_THROWS(func_throw_int(), int)
		TEST_THROWS_ANYTHING(func_throw_int())
		
		// Will fail since func_throw_int() throws an int (not a float)
		//
		TEST_THROWS_MSG(func_throw_int(), float, "func_throw_int() throws an int, expected a float exception")
		TEST_THROWS_NOTHING_MSG(func_throw_int(), "func_throw_int() throws an int, expected no exception at all")
	}
	
	void func() {}
	void func_no_throw() throw() {}
	void func_throw_int() throw(int) { throw 13; }
};

enum OutputType
{
	Compiler,
	Html,
	TextTerse,
	TextVerbose
};

static void
usage()
{
	cout << "usage: mytest [MODE]\n"
		 << "where MODE may be one of:\n"
		 << "  --compiler\n"
		 << "  --html\n"
		 << "  --text-terse (default)\n"
		 << "  --text-verbose\n";
	exit(0);
}

static auto_ptr<Test::Output>
cmdline(int argc, char* argv[])
{
	if (argc > 2)
		usage(); // will not return
	
	Test::Output* output = 0;
	
	if (argc == 1)
		output = new Test::TextOutput(Test::TextOutput::Verbose);
	else
	{
		const char* arg = argv[1];
		if (strcmp(arg, "--compiler") == 0)
			output = new Test::CompilerOutput;
		else if (strcmp(arg, "--html") == 0)
			output =  new Test::HtmlOutput;
		else if (strcmp(arg, "--text-terse") == 0)
			output = new Test::TextOutput(Test::TextOutput::Terse);
		else if (strcmp(arg, "--text-verbose") == 0)
			output = new Test::TextOutput(Test::TextOutput::Verbose);
		else
		{
			cout << "invalid commandline argument: " << arg << endl;
			usage(); // will not return
		}
	}
	
	return auto_ptr<Test::Output>(output);
}

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// Main test program
//
int main(int argc, char* argv[])
{
	try
	{
	strcpy(szTitle, "WebAggregator");
	strcpy(szWindowClass, "WebAggregator");

	MSG msg = {0};

	MyRegisterClass(NULL);

    // Perform application initialization:
    if (!InitInstance (NULL, SW_SHOW))
        return FALSE;

	// Init COM
    OleInitialize(NULL);
	
	HMODULE webKitMod = ::LoadLibrary("WebKit.dll");
	MyDllGetClassObject regFunc = (MyDllGetClassObject)::GetProcAddress(webKitMod, "DllGetClassObject");
	IClassFactory* factory = NULL;
	HRESULT hr = regFunc(CLSID_WebView, IID_IClassFactory, (void**)&factory);
	if (FAILED(hr))
       return 0;

	hr = factory->CreateInstance(0, IID_IWebView, (void **)&gWebView);
	if (FAILED(hr))
        return 0;
	
    WebKitNativeController* gWebHost = new WebKitNativeController();
    hr = gWebView->setFrameLoadDelegate(gWebHost);
    if (FAILED(hr))
        return 0;

    hr = gWebView->setHostWindow((int)hwndMain);
    if (FAILED(hr))
        return 0;
	
    tagRECT clientRect;
    GetClientRect(hwndMain, &clientRect);
	BSTR a = NULL;
    hr = gWebView->initWithFrame(clientRect, a, a);
    if (FAILED(hr))
        return 0;
		
	IWebFrame* frame;
    frame = gWebView->mainFrame();
    if (frame == NULL)
        return 0;

    static BSTR defaultHTML = 0;
    defaultHTML = L"<p style=\"background-color: #00FF00\">Testing</p><img src=\"http://webkit.org/images/icon-gold.png\" alt=\"Face\"><div style=\"border: solid blue\" contenteditable=\"true\">div with blue border</div><ul><li>foo<li>bar<li>baz</ul>";

	frame->loadHTMLString(defaultHTML, "0");
    //frame->Release();

    hr = gWebView->QueryInterface(IID_IWebViewPrivate, (void**)&gWebViewPrivate);
    if (FAILED(hr))
        return 0;

	hr = gWebViewPrivate->setTransparent(false);
    if (FAILED(hr))
        return 0;

    gViewWindow = (HWND)gWebViewPrivate->viewWindow();
    if (FAILED(hr) || !gViewWindow)
        return 0;

    //resizeViews();
    IWebMutableURLRequest* request = 0;
    static BSTR methodBStr = SysAllocString(L"GET");

	frame = gWebView->mainFrame();
    if (frame == NULL)
        return 0;
	
	regFunc = (MyDllGetClassObject)::GetProcAddress(webKitMod, "DllGetClassObject");
	
	factory = NULL;
	hr = regFunc(CLSID_WebMutableURLRequest, IID_IClassFactory, (void**)&factory);
	if (FAILED(hr))
        return 0;

	hr = factory->CreateInstance(0, IID_IWebMutableURLRequest, (void **)&request);
	if (FAILED(hr))
        return 0;
	
		// Demonstrates the ability to use multiple test suites
		//
		Test::Suite ts;
		//ts.add(auto_ptr<Test::Suite>(new FailTestSuite));
		ts.add(auto_ptr<Test::Suite>(new CompareTestSuite));
		//ts.add(auto_ptr<Test::Suite>(new ThrowTestSuite));

		// Run the tests
		//
		//int argc = 0;
		//char* argv[1] = {""};
		auto_ptr<Test::Output> output(cmdline(argc, argv));
		
		BSTR urlBStr = L"http://yahoo.com";
		

		hr = request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60);
		if (FAILED(hr))
			return 0;

		hr = request->setHTTPMethod(methodBStr);
		if (FAILED(hr))
			return 0;

		hr = frame->loadRequest(request);
		if (FAILED(hr))
			return 0;

		while(GetMessage(&msg, NULL, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			while(gWebView->isLoading() == false)
			{
				PostQuitMessage(0);
				break;
			}
		}

		domDoc = gWebView->mainFrame()->DOMDocument();
			
		ts.run(*output, true);
		
		gWebView->Release();

		Test::HtmlOutput* const html = dynamic_cast<Test::HtmlOutput*>(output.get());
		if (html)
			html->generate(cout, true, "MyTest");

		system("PAUSE");

		::FreeLibrary(webKitMod);
		OleUninitialize();
	}
	catch (...)
	{
		cout << "unexpected exception encountered\n";
		return EXIT_FAILURE;
	}
	
	
	return EXIT_SUCCESS;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, NULL);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = 0;
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, NULL);

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    hwndMain = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
                   0, 0, 800, 600, 0, 0, hInstance, 0);

    if (!hwndMain)
        return FALSE;

    ShowWindow(hwndMain, SW_HIDE);

    return TRUE;
}